package com.wxw.BaseLibrary.element;

import com.wxw.BaseLibrary.tool.FileUtil;
import com.wxw.BaseLibrary.tool.L;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Stack;

/**
 * Created by david on 2014-05-29.
 */
public class JsonObject {
    private HashMap<String, JsonProperty> _property;

    public JsonObject()
    {
        this._property = null;
    }

    public JsonObject(String jsonString)
    {
        try {
            parse(jsonString);
        } catch (Exception e){

        }
    }

    public JsonObject(IJsonSetProperties callback)
    {
        if (callback != null)
        {
            callback.setProperties(this);
        }
    }

    /**
     * Json字符串解析
     * @param jsonString
     * @throws Exception
     */
    private void parse(String jsonString) throws Exception
    {
        jsonString = jsonString.replace("\t", "");
        jsonString = jsonString.replace("\n", "");
        jsonString = jsonString.replace("\r", "");
        int len = jsonString.length();

        if (jsonString==null || "".equals(jsonString) || jsonString.charAt(0) != '{' || jsonString.charAt(jsonString.length() - 1) != '}')
        {
            throw new Exception("传入文本不符合Json格式!" + jsonString);
        }
        Stack<Character> stack = new Stack<Character>();
        Stack<Character> stackType = new Stack<Character>();
        StringBuilder sb = new StringBuilder();
        char cur;
        //True表示当前内容都为字符串值
        boolean convert = false;
        //True表示当前内容都为值
        boolean isValue = false;
        JsonProperty last = null;
        for (int i = 1; i <= len - 2; i++)
        {
            cur = jsonString.charAt(i);
            if (cur == '}')
            {
                ;
            }
            if (Character.isSpaceChar(cur) && stack.size() == 0 && stackType.size() == 0)
            {
                sb.append(cur);
            }
            else if ((cur == '\'' || cur == '\"') && !convert && stack.size() == 0 && stackType.size() == 0)
            {
                if (isValue) {
                    sb.setLength(0);
                    sb.append(cur);
                }
                convert = true;
                stack.push(cur);
            }
            else if ((cur == '\'' || cur == '\"') && convert && stack.size() > 0 && stack.peek().equals(cur) && stackType.size() == 0)
            {
                stack.pop();
                convert = false;
                if (isValue) {
                    sb.append(cur);
                }
            }
            else if ((cur == '[' || cur == '{') && stack.size() == 0)
            {
                stackType.push(cur == '['? ']' : '}');
                sb.append(cur);
            }
            else if ((cur == ']' || cur == '}') && stackType.size() > 0 && stackType.peek().equals(cur))
            {
                stackType.pop();
                sb.append(cur);
                if (stack.size() == 0 && stackType.size() == 0)
                {
                    if (last != null && isValue)
                    {
                        String temp = sb.toString();
                        last.parse(temp);
                    }
                    isValue = false;
                    sb.setLength(0);
                }
            }
            else if (cur == ':' && stack.size() == 0 && stackType.size() == 0 && !isValue && !convert)
            {
                last = new JsonProperty();
                setProperty(sb.toString(), last);
                isValue = true;
                sb.setLength(0);
            }
            else if (cur == ',' && stack.size() == 0 && stackType.size() == 0 && !convert)
            {
                if (last != null && isValue)
                {

                    String temp = sb.toString();
                    last.parse(temp);
                }
                isValue = false;
                sb.setLength(0);
            }
            else
            {
                sb.append(cur);
            }
        }
        if (sb.length() > 0 && last != null && last.getType() == JsonPropertyType.Null)
        {
            String temp = sb.toString();
            last.parse(temp);
        }
    }

    /**
     * 获取或设置属性
     * @param PropertyName
     * @return
     */
    public JsonProperty getProperty(String PropertyName){
        JsonProperty result = null;
        if (this._property != null && this._property.containsKey(PropertyName.trim()))
        {
            result = this._property.get(PropertyName.trim());
        }
        return result;
    }

    public void setProperty(String PropertyName, JsonProperty property){
        if (this._property == null)
        {
            this._property = new HashMap<String, JsonProperty>();
        }
        this._property.put(PropertyName.trim(), property);
    }

    public void addProperty(String propertyName, Object o) {
        JsonProperty property = new JsonProperty(o);
        setProperty(propertyName, property);
    }

    /**
     * 通过此泛型函数可直接获取指定类型属性的值
     * @param PropertyName
     * @param <T>
     * @return
     */
    public <T extends Class> T getObjectProperty(String PropertyName){
        JsonProperty p = getProperty(PropertyName);
        if (p != null)
        {
            return p.getTValue();
        }
        return null;
    }

    /**
     * 获取属性名称列表
     * @return
     */
    public String[] getPropertyNames()
    {
        if (this._property == null) {
            return null;
        }
        String[] keys = null;
        if (this._property.size() > 0)
        {
            keys = new String[this._property.size()];
            this._property.keySet().toArray(keys);
        }
        return keys;
    }

    /**
     * 移除一个属性
     * @param PropertyName
     * @return
     */
    public JsonProperty removeProperty(String PropertyName)
    {
        if (this._property != null && this._property.containsKey(PropertyName))
        {
            JsonProperty p = this._property.get(PropertyName);
            this._property.remove(PropertyName);
            return p;
        }
        return null;
    }

    /**
     * 是否为空对象
     * @return
     */
    public boolean isNull()
    {
        return this._property == null;
    }

    @Override
    public String toString()
    {
        return this.toString(0);
    }

    /**
     * ToString...
     * @param level
     * @return
     */
    public String toString(int level)
    {
        if (this.isNull())
        {
            return "{}";
        }
        else
        {
            StringBuilder sb = new StringBuilder();
            if (level > 0)
            {
                sb.append((level > 1 ? "\n" : "") + getFormatStr(level - 1) + "{");
                for (String key : this._property.keySet())
                {
                    sb.append((level > 0 ? "\n" : "") + getFormatStr(level));
                    sb.append("\""+key+"\"").append(":");
                    sb.append(this._property.get(key).toString(level + 1));
                    sb.append(",");
                }
                if (sb.length() > 1) {
                    sb.deleteCharAt(sb.length() - 1);
                }
                sb.append((level > 0 ? "\n" : "") + getFormatStr(level - 1) + "}");
            }
            else
            {
                sb.append("{");
                for (String key : this._property.keySet())
                {
                    sb.append("\""+key+"\"").append(": ");
                    sb.append(this._property.get(key).toString());
                    sb.append(",");
                }
                if (sb.length() > 1) {
                    sb.deleteCharAt(sb.length() - 1);
                }
                sb.append("}");
            }
            return sb.toString();
        }
    }

    public static String getFormatStr(int level)
    {
        String s = "";
        for (int i = 0; i < level; i++)
        {
            s += "\t";
        }
        return s;
    }

    public static JsonObject readFile(String path) {
        try {
            String s = FileUtil.ReadFile(path);
            if (!"".equals(s.trim())) {
                return new JsonObject(s);
            }
        } catch (Exception e) {
            L.WriteExceptionLog("JsonObject", "readFile", e.getMessage());
        }
        return null;
    }

    public static String getJsonPropertyValue(JsonObject jsonObject, String name, String defValue) {
        JsonProperty jsonProperty = jsonObject.getProperty(name);
        if (jsonProperty == null) {
            return defValue;
        }
        return jsonProperty.getValue();
    }

    public static String getJsonPropertyValue(JsonProperty jsonProperty, String name, String defValue) {
        JsonProperty jp = jsonProperty.getProperty(name);
        if (jp == null) {
            return defValue;
        }
        return jp.getValue();
    }

    public static int getJsonPropertyIntegerValue(JsonObject jsonObject, String name, int defValue) {
        JsonProperty jp = jsonObject.getProperty(name);
        if (jp == null) {
            return defValue;
        }
        return jp.getInteger();
    }

    public static int getJsonPropertyIntegerValue(JsonProperty jsonProperty, String name, int defValue) {
        JsonProperty jp = jsonProperty.getProperty(name);
        if (jp == null) {
            return defValue;
        }
        return jp.getInteger();
    }

    public static double getJsonPropertyDoubleValue(JsonObject jsonObject, String name, double defValue) {
        JsonProperty jp = jsonObject.getProperty(name);
        if (jp == null) {
            return defValue;
        }
        return jp.getNumber();
    }

    public static double getJsonPropertyDoubleValue(JsonProperty jsonProperty, String name, double defValue) {
        JsonProperty jp = jsonProperty.getProperty(name);
        if (jp == null) {
            return defValue;
        }
        return jp.getNumber();
    }

    public static JsonObject mapToJson(Map<String, String> data) {
        Iterator<String> keys = data.keySet().iterator();
        JsonObject json = new JsonObject();
        while(keys.hasNext()) {
            String key = keys.next();
            json.setProperty(key, new JsonProperty(data.get(key)));
        }
        return json;
    }
}
